package jmind.core.support;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import jmind.base.util.DateUtil;
import org.apache.commons.beanutils.*;


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.Date;
import java.util.Map;

public class MethodSupport {
    //  http://blog.csdn.net/pengchua/article/details/1947164

    /**
     * 由于 ConvertUtils 解析时不不支持 java.util.Date 所以需要特许处理
     * ConvertUtilsBean
     */
    static {
        ConvertUtils.register(new Converter() {
            @SuppressWarnings("unchecked")
            public <T> T convert(Class<T> type, Object value) {
                if (value instanceof String)
                    return (T) DateUtil.parse(value.toString());
                return type.cast(value);
            }

        }, Date.class);
        // see  DateConverter
        //        DateConverter dateConverter = new DateConverter();
        //        dateConverter.setPattern(DateUtil.DATE_PATTERN);
        //        ConvertUtils.register(dateConverter, Date.class);

        // ConvertUtilsBean  & StringConverter
        ConvertUtils.register(new Converter() {
            @SuppressWarnings("unchecked")
            public <T> T convert(Class<T> type, Object value) {
                if (value instanceof Date) {
                    return (T) DateUtil.format((Date) value, DateUtil.F24_PATTERN);
                }
                return type.cast(value.toString());
            }
        }, String.class);
    }

    /**
     * 获取属性，返回String
     *
     * @param bean
     * @param name
     * @return
     */
    public static String getProperty(Object bean, String name) {
        try {
            return BeanUtils.getProperty(bean, name);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null ;

    }

    /**
     * 获取属性，返回原始type
     *
     * @param bean
     * @param name
     * @return
     */
    public static Object getPropertyValue(Object bean, String name) {
        try {
            return  PropertyUtils.getProperty(bean, name);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 设置属性值
     *
     * @param bean
     * @param name
     * @param value 基本数据类型,Date等都支持 String格式
     */
    public static void setProperty(Object bean, String name, Object value) {
        try {
            BeanUtils.setProperty(bean, name, value);
          //  PropertyUtils.setProperty(bean, name, value);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }

    /**
     * 获取方法参数名称，匹配同名的某一个方法
     *
     * @param clazz
     * @param method
     * @return
                如果类或者方法不存在
     * @throws javassist.NotFoundException
            如果最终编译的class文件不包含局部变量表信息
     */
    public static String[] getMethodParamNames(Class<?> clazz, String method) throws Exception {

        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get(clazz.getName());
        CtMethod cm = cc.getDeclaredMethod(method);
        return getMethodParamNames(cm);
    }

    /**
     * 获取方法参数名称
     *
     * @param cm
     * @return
     * @throws
     * @throws javassist.NotFoundException
              如果最终编译的class文件不包含局部变量表信息
     */
    protected static String[] getMethodParamNames(CtMethod cm) throws Exception {

        MethodInfo methodInfo = cm.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        if (attr == null)
            return null;

        String[] paramNames = new String[cm.getParameterTypes().length];
        int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
        for (int i = 0; i < paramNames.length; i++)
            paramNames[i] = attr.variableName(i + pos);
        return paramNames;
    }

    /**
     * map 转换成bean ，比直接封装String 可以转换成对应的类型
     *
     * @param map
     * @param clazz
     * @return
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws NoSuchMethodException
     * @see BeanUtils
     */
    public static <T> T map2Object(Map<String, Object> map, Class<T> clazz) throws InstantiationException,
            IllegalAccessException {
        T obj = clazz.newInstance();
        for (String name : map.keySet()) {
            Class<?> type = null;
            try {
                type = PropertyUtils.getPropertyType(obj, name);
                if (type != null) {
                    // 设置参数
                    PropertyUtils.setProperty(obj, name, ConvertUtils.convert(map.get(name), type));
                }
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }

        }
        // beanutils 方法
        //        Map<String, String> describe = BeanUtils.describe(obj);
        //        BeanUtils.populate(obj, describe);
        return obj;
    }

    /**
     * BeanUtils它以字符串的形式对javabean进行转换，而PropertyUtils是以原本的类型对javabean进行操作,
     * 如果类型不对，就会有argument type mismatch异常。
     * PropertyUtils.getPropety方法获得的属性值的类型为该属性本来的类型，
     * 而BeanUtils.getProperty则是将该属性的值转换成字符串后才返回。
     * BeanUtils 不允许对象的属性值为 null (基本类型对应的包装类可)，
     * PropertyUtils 可以拷贝属性值 null 的对象。如果对象属性值为 null，
     * BeanUtils.copyProperties 方法会报 commons.beanutils.ConversionException: No value specified 错误；
     * 但当Ineger、Boolean、Long等基本类型包装类为null时，会将值复制后转换成0 或 false。
     *
     * @param map
     * @param clazz
     * @return
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public static <T> T mapStr2Object(Map<String, String> map, Class<T> clazz) throws InstantiationException,
            IllegalAccessException {
        T obj = clazz.newInstance();
        for (String name : map.keySet()) {
            try {
                BeanUtils.setProperty(obj, name, map.get(name));
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
        // beanutils 方法
        //        Map<String, String> describe = BeanUtils.describe(obj);
        //        BeanUtils.populate(obj, describe);
        return obj;
    }

}
